package com.dss.code_generation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dss.code_generation.config.DssProperties;
import com.dss.code_generation.config.WebsocketEndPoint;
import com.dss.code_generation.domain.Tables;
import com.dss.code_generation.dto.AutoDto;
import com.dss.code_generation.dto.SelectDto;
import com.dss.code_generation.dto.WebSocketMsgDto;
import com.dss.code_generation.mapper.TablesDao;
import com.dss.code_generation.service.ITablesService;
import com.dss.code_generation.util.*;
import com.dss.code_generation.util.converter.TablesConverter;
import com.dss.code_generation.vo.AutoCodeMsgVo;
import com.dss.code_generation.vo.TablesVo;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description:
 * @ClassName: TablesServiceImpl
 * @author: dongshoushan
 * @date: 2020年06月08日 10:16
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class TablesServiceImpl extends ServiceImpl<TablesDao, Tables> implements ITablesService {
    @Resource
    private TablesDao tablesDao;
    @Resource
    private DssProperties dssProperties;
    @Resource
    private AutoCodeUtils autoCodeUtils;
    @Resource
    private TablesConverter tablesConverter;
    @Resource
    private HttpServletResponse response;
    @Resource
    private WebsocketEndPoint websocketEndPoint;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private static final String[] LENGTH_REQUIRED = new String[]{"VARCHAR", "CHAR"};

    /**
     * 查询所有未生成的表
     *
     * @return
     */
    @Override
    public List<TablesVo> getAll() {
        //查询所有表名
        List<TablesVo> all = tablesDao.getAll();
        List<String> ignoreAddList = Arrays.asList(dssProperties.getIgnoreAdd());
        //过滤
        List<TablesVo> result = new ArrayList<>();
        for (TablesVo vo : all) {
            Boolean add = true;
            for (String s : ignoreAddList) {
                if (vo.getTableName().startsWith(s) || result.contains(vo)) {
                    add = false;
                }
            }
            if (add) {
                result.add(vo);
            }
        }
        all = result;
        List<Tables> tablesList = tablesDao.selectList(null);
        //查询所有已生成的表
        List<TablesVo> voList = tablesConverter.convert(tablesList, TablesVo.class);
        //所有已生成的表表名
        List<String> collectAdd = new ArrayList<>();
        if (tablesList != null && tablesList.size() > 0) {
            collectAdd = voList.stream().map(tablesVo -> tablesVo.getTableName()).collect(Collectors.toList());
        }
        //所有未生成的表
        List<String> finalCollectAdd = collectAdd;
        List<TablesVo> list = all.stream().filter(tablesVo -> !finalCollectAdd.contains(tablesVo.getTableName())).collect(Collectors.toList());
        return list;
    }

    /**
     * 新生成代码
     *
     * @param autoDto
     * @return
     */
    @Override
    public Boolean add(AutoDto autoDto) {
        threadPoolTaskExecutor.execute(()->{
            String tablesName = autoDto.getTableName();
            autoDto.setAbsolutePathWeb(autoDto.getAbsolutePathWeb().replace("\\", "/"));
            autoDto.setContentPathWeb(autoDto.getContentPathWeb().replace("\\", "/"));
            List<Tables> list = tablesDao.getByTablesName(Arrays.asList(tablesName.split(",")));
            for (int i = 0; i < list.size(); i++) {
                Tables tables = list.get(i);
                AutoCodeMsgVo msgVo = AutoCodeMsgVo.builder().tableName(tables.getTableName())
                        .tableComment(tables.getTableComment())
                        .speed(StringUtils.getRateWithPercentSign(i+1,list.size())).build();
                try {
                    autoCodeUtils.generate(autoDto, tables);
                    tables.setFlag(1);
                    tables.setAbsolutePathWeb(autoDto.getAbsolutePathWeb());
                    tables.setContentPathWeb(autoDto.getContentPathWeb());
                    tables.setAuthor(autoDto.getAuthor());
                    tables.setCreateDate(new Date());
                    tables.setModule(autoDto.getModule());
                    tables.setMethod(autoDto.getMethod());
                    tablesDao.insert(tables);
                    WebSocketMsgDto dto = WebSocketMsgDto.builder().cmd("auto_code_speed").data(msgVo).build();
                    websocketEndPoint.send(JacksonUtils.toString(dto));
                }catch (Exception e){
                    log.error(StringUtils.format("生成代码失败，表名：{}，错误信息：{}",tablesName,e.getMessage()));
                    msgVo.setErrorMsg(e.getMessage());
                    WebSocketMsgDto dto = WebSocketMsgDto.builder().cmd("auto_code_speed").data(msgVo).build();
                    websocketEndPoint.send(JacksonUtils.toString(dto));
                }
            }
        });
        return true;
    }

    /**
     * 查询所有已生成的表
     *
     * @param pageReq
     * @return
     */
    @Override
    public Page<TablesVo> getList(SelectDto pageReq) {
        Page<Tables> tablesPage = new Page<>(pageReq.getPage(), pageReq.getLimit());
        QueryWrapper<Tables> tablesQueryWrapper = new QueryWrapper<>();
        String content = pageReq.getContent();
        if (!StringUtils.isEmpty(content)) {
            tablesQueryWrapper.like("table_name", content).or().like("table_comment", content);
        }
        tablesQueryWrapper.orderByDesc("create_date");
        Page<Tables> pageSelectResult = tablesDao.selectPage(tablesPage, tablesQueryWrapper);
        List<Tables> records = pageSelectResult.getRecords();
        List<TablesVo> voList = tablesConverter.convert(records, TablesVo.class);
        Page<TablesVo> page = new Page<>();
        page.setRecords(voList);
        page.setTotal(pageSelectResult.getTotal());
        page.setSize(pageSelectResult.getSize());
        return page;
    }

    /**
     * 忽略生成代码
     *
     * @param autoDto
     * @return
     */
    @Override
    public Boolean ignoreAdd(AutoDto autoDto) {
        String tablesName = autoDto.getTableName();
        List<Tables> list = tablesDao.getByTablesName(Arrays.asList(tablesName.split(",")));
        for (Tables tables : list) {
            tables.setFlag(2);
            tablesDao.insert(tables);
        }
        return true;
    }

    /**
     * 重新生成代码
     *
     * @param list
     * @return
     */
    @Override
    public Boolean regenerate(List<AutoDto> list) {
        threadPoolTaskExecutor.execute(()->{
        for (int i = 0; i < list.size(); i++) {
            AutoDto autoDto = list.get(i);
            Tables tables = tablesDao.selectById(autoDto.getId());
            AutoCodeMsgVo msgVo = AutoCodeMsgVo.builder().tableName(tables.getTableName())
                    .tableComment(tables.getTableComment())
                    .speed(StringUtils.getRateWithPercentSign(i + 1, list.size())).build();
            try {
                tables.setCreateDate(new Date());
                tablesDao.updateById(tables);
                autoCodeUtils.generate(autoDto, tables);
                WebSocketMsgDto dto = WebSocketMsgDto.builder().cmd("auto_code_speed").data(msgVo).build();
                websocketEndPoint.send(JacksonUtils.toString(dto));
            }catch (Exception e){
                log.error(StringUtils.format("生成代码失败，表名：{}，错误信息：{}",tables.getTableName(),e.getMessage()));
                msgVo.setErrorMsg(e.getMessage());
                WebSocketMsgDto dto = WebSocketMsgDto.builder().cmd("auto_code_speed").data(msgVo).build();
                websocketEndPoint.send(JacksonUtils.toString(dto));
            }
        }
        });
        return true;
    }

    /**
     * 导出表格结构excel数据
     *
     * @return {@link List}<{@link TablesVo}>
     */
    @Override
    public List<TablesVo> exportTableStructureExcelData() {
        //查询所有表名
        List<TablesVo> all = tablesDao.getAll();
        List<String> ignoreAddList = Arrays.asList(dssProperties.getIgnoreExportPrefix());
        //过滤
        List<TablesVo> result = new ArrayList<>();
        for (TablesVo vo : all) {
            Boolean add = true;
            for (String s : ignoreAddList) {
                if (vo.getTableName().startsWith(s) || result.contains(vo)) {
                    add = false;
                }
            }
            if (add) {
                result.add(vo);
            }
        }
        return result;
    }

    /**
     * 导出excel任务
     */
    @Override
    public void exportExcelTask(String tableName) throws Exception {
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet("导出mysql表设计");
        //11列
        for (int i = 0; i < 9; i++) {
            sheet.setColumnWidth(i, 10 * 250);
        }
        sheet.setColumnWidth(1, 25 * 250);
        sheet.setColumnWidth(2, 20 * 250);
        sheet.setColumnWidth(7, 10 * 250);
        sheet.setColumnWidth(8, 80 * 250);
        List<Tables> list = tablesDao.getByTablesName(Arrays.asList(tableName.split(",")));
        int firstRow = 0;
        int secondRow = 1;
        int rowNum = 2;
        for (Tables tables : list) {
            List<ColumnClass> columnList = autoCodeUtils.getColumnList(tables.getTableName());
            String[] headers = {"序号", "字段名称", "字段标识", "字段属性", "字段类型", "字段长度", "允许空", "默认值", "备注"};
            XSSFCellStyle headerStyle = workbook.createCellStyle();
            XSSFCellStyle boderStyle = workbook.createCellStyle();
            //合并单元格 起始行,结束行,起始列,结束列
            // 居中
            boderStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            boderStyle.setAlignment(HorizontalAlignment.CENTER);
            boderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 设定填充单色
            boderStyle.setFillForegroundColor((short) 41);
            XSSFRow row0 = sheet.createRow(firstRow);
            row0.setHeightInPoints(25);
            for (int i = 0; i < 9; i++) {
                Cell oneCell0 = row0.createCell(i);
                // 设置第一层标题内容
                oneCell0.setCellValue(StringUtils.format("{}【{}】", tables.getTableComment(), tables.getTableName()));
                oneCell0.setCellStyle(boderStyle);

            }
            CellRangeAddress callRangeAddress0 = new CellRangeAddress(firstRow, firstRow, 0, 8);
            sheet.addMergedRegion(callRangeAddress0);
            // 设置单元格边框
            headerStyle.setBorderTop(BorderStyle.THIN); // 上边框
            headerStyle.setBorderBottom(BorderStyle.THIN); // 下边框
            headerStyle.setBorderLeft(BorderStyle.THIN); // 左边框
            headerStyle.setBorderRight(BorderStyle.THIN);  // 右边框
            //合并单元格 起始行,结束行,起始列,结束列
            // 居中
            headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            headerStyle.setAlignment(HorizontalAlignment.CENTER);
            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 设定填充单色
            headerStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());

            XSSFRow row = sheet.createRow(secondRow);
            row.setHeightInPoints(25);
            // 在excel表中添加表头
            for (int i = 0; i < headers.length; i++) {
                XSSFCell cell = row.createCell(i);
                XSSFRichTextString text = new XSSFRichTextString(headers[i]);
                cell.setCellValue(text);
                cell.setCellStyle(headerStyle);
            }

            XSSFCellStyle bodyStyle = workbook.createCellStyle();
            // 内容样式：居中
            bodyStyle.setBorderTop(BorderStyle.THIN); // 上边框
            bodyStyle.setBorderBottom(BorderStyle.THIN); // 下边框
            bodyStyle.setBorderLeft(BorderStyle.THIN); // 左边框
            bodyStyle.setBorderRight(BorderStyle.THIN);  // 右边框
            bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            bodyStyle.setAlignment(HorizontalAlignment.CENTER);
            //内容样式：靠左对齐样式
            XSSFCellStyle cellStyleLeft = workbook.createCellStyle();
            cellStyleLeft.setAlignment(HorizontalAlignment.LEFT);
            cellStyleLeft.setBorderTop(BorderStyle.THIN); // 上边框
            cellStyleLeft.setBorderBottom(BorderStyle.THIN); // 下边框
            cellStyleLeft.setBorderLeft(BorderStyle.THIN); // 左边框
            cellStyleLeft.setBorderRight(BorderStyle.THIN);  // 右边框
            cellStyleLeft.setVerticalAlignment(VerticalAlignment.CENTER);
            for (int i = 0; i < columnList.size(); i++) {
                ColumnClass columnClass = columnList.get(i);
                XSSFRow row1 = sheet.createRow(rowNum);
                row1.setHeightInPoints(25);
                XSSFCell cell = row1.createCell(0);
                cell.setCellStyle(bodyStyle);
                cell.setCellValue(i + 1);
                XSSFCell cell1 = row1.createCell(1);
                cell1.setCellStyle(bodyStyle);
                String columnComment = columnClass.getColumnComment();
                if (columnComment.contains("：")){
                    columnComment = columnComment.replace("：", ":");
                }
                if (columnComment.contains(":")) {
                    String[] split = columnComment.split(":");
                    columnComment = split[0];
                }
                cell1.setCellValue(columnComment);
                XSSFCell cell2 = row1.createCell(2);
                cell2.setCellStyle(bodyStyle);
                cell2.setCellValue(columnClass.getColumnName());
                XSSFCell cell3 = row1.createCell(3);
                cell3.setCellStyle(bodyStyle);
                cell3.setCellValue("id".equals(columnClass.getColumnName()) ? "pk" : "");
                XSSFCell cell4 = row1.createCell(4);
                cell4.setCellStyle(bodyStyle);
                cell4.setCellValue(columnClass.getColumnType());
                XSSFCell cell5 = row1.createCell(5);
                cell5.setCellStyle(bodyStyle);
                List<String> stringList = Arrays.asList(LENGTH_REQUIRED);
                cell5.setCellValue(stringList.contains(columnClass.getColumnType()) ? columnClass.getColumnSize() + "" : "");
                XSSFCell cell6 = row1.createCell(6);
                cell6.setCellStyle(bodyStyle);
                cell6.setCellValue(columnClass.getRequired() ? "N" : "Y");
                XSSFCell cell7 = row1.createCell(7);
                cell7.setCellStyle(bodyStyle);
                cell7.setCellValue(columnClass.getColumnDefault());
                XSSFCell cell8 = row1.createCell(8);
                cell8.setCellStyle(cellStyleLeft);
                cell8.setCellValue(columnClass.getColumnComment().contains(":") || columnClass.getColumnComment().contains("：") ? columnClass.getColumnComment() : "");
                rowNum++;
                firstRow++;
                secondRow++;
            }
            rowNum += 4;
            secondRow += 4;
            firstRow += 4;
        }

        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition",
                "attachment;filename=" + java.net.URLEncoder.encode("mysql表设计导出.xlsx", "UTF-8"));
        response.flushBuffer();
        workbook.write(response.getOutputStream());
    }

    /**
     * 删除其他表
     */
    @Override
    public int deleteOtherTable() {
        //查询所有表名
        List<TablesVo> all = tablesDao.getAll();
        List<String> ignoreAddList = Arrays.asList(dssProperties.getIgnoreExportPrefix());
        //过滤
        List<TablesVo> result = new ArrayList<>();
        String stringBuffer;
        for (TablesVo vo : all) {
            Boolean add = true;
            for (String s : ignoreAddList) {
                if (vo.getTableName().startsWith(s) || result.contains(vo)) {
                    add = false;
                }
            }
            if (add) {
                result.add(vo);
            }
        }
        if (result.size()==0){
            return 0;
        }
        stringBuffer = result.stream().map(tablesVo -> StringUtils.format("DROP TABLE IF EXISTS \"public\".\"{}\";", tablesVo.getTableName())).collect(Collectors.joining());
        System.out.println(stringBuffer);
        tablesDao.runSql(stringBuffer);
        return result.size();
    }
}
